package com.aididi.concurrent;


import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * @Auther: zx.xu
 * @Description:
 */
public class CompletableFutureDemo {

    public static void main(String[] args) throws Exception {

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-d hh:mm:ss");

        // Future在执行结束后没法回调，调用get方法会被阻塞；CompletableFuture调用get方法获取结果也会被阻塞。
        System.out.println("future start : "+ formatter.format(LocalDateTime.now()));
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<?> future = executor.submit(() -> {
            Thread.sleep(1000);
            return "future finish!!";
        });
        System.out.println(future.get() + " at " + formatter.format(LocalDateTime.now()) );



        //async 代表异步。
        //runAsync 和 supplyAsync 方法的区别在于，前者没有结果返回，后者会有结果返回
        System.out.println("completableFuture start : "+ formatter.format(LocalDateTime.now()));
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "CompletableFuture finish!!";
        });
        System.out.println(completableFuture.get() + " at " + formatter.format(LocalDateTime.now()) );
        System.out.println("主线程会被阻塞,时间："+ formatter.format(LocalDateTime.now()));


        //但是CompletableFuture可以回调，可不通过get方法获取结果。
        System.out.println("completableFuture start : "+ formatter.format(LocalDateTime.now()));
        CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "CompletableFuture finish!!";
        }).whenComplete((v, e) -> {
            System.out.println(v + " at " + formatter.format(LocalDateTime.now()));
        });
        System.out.println("主线程会被阻塞,时间："+ formatter.format(LocalDateTime.now()));


        //Future模式执行批量任务，在完成任务后要想执行其它任务得通过get方法获取结果，在依次遍历Future列表时，
        // 各个get方法依赖于Future列表顺序。
        //由于Future的get()方法会阻塞，导致最终休眠时间短的线程没有先退出
        ArrayList<Future<String>> futureList = new ArrayList<>();
        Random random = new Random();
        for (int i = 0; i < 3; i++) {
            futureList.add(executor.submit(() -> {
                String threadName = Thread.currentThread().getName();
                int cnt = random.nextInt(5000);
                System.out.println(threadName + " 开始执行时间：" +formatter.format(LocalDateTime.now()));
                System.out.println(threadName + " 将睡眠 " + cnt + "毫秒");
                Thread.sleep(cnt);
                return threadName;
            }));
        }
        futureList.forEach( x->{
            try {
                System.out.println(x.get() + "执行结束时间: " + formatter.format(LocalDateTime.now()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        });

        // CompletableFuture模式通过回调可以得到执行结果，各自完成任务可以立即执行接下来的任务。

        for (int i = 0; i < 3; i++) {
            CompletableFuture.supplyAsync(() ->{
                String threadName = Thread.currentThread().getName();
                int cnt = random.nextInt(5000);
                System.out.println(threadName + " 开始执行时间：" +formatter.format(LocalDateTime.now()));
                System.out.println(threadName + " 将睡眠 " + cnt + "毫秒");
                try {
                    Thread.sleep(cnt);
                }catch (Exception e){
                    e.printStackTrace();
                }
                return threadName;
            }).whenComplete((v,e) ->{
                System.out.println(v + " 执行结束时间: " + formatter.format(LocalDateTime.now()));
            });
        }

        //通过实现CompletionStage接口，CompletableFuture对象可以级联地执行任务
        CompletableFuture.supplyAsync(() ->{
            String threadName = Thread.currentThread().getName();
            try {
                int cnt = random.nextInt(5000);
                System.out.println(threadName + " 开始执行时间：" +formatter.format(LocalDateTime.now()));
                System.out.println(threadName + " 将睡眠 " + cnt + "毫秒");
                Thread.sleep(cnt);
            }catch (Exception e){
                e.printStackTrace();
            }
            return threadName;
        }).thenApply((t) ->{
            System.out.println(" thenApply ");
            System.out.println(" thenApply接收的supplyAsync返回值： " + t);
            return "122";
        }).thenAccept((t) ->{       //只对结果进行消费，没有返回值
            System.out.println(" thenAccept ");
            System.out.println(" thenAccept接收的thenApply返回值： " + t);
        }).thenRunAsync(() ->{
            System.out.println(" thenRunAsync ");
        });


        //可以通过anyOf方法得到多个任务中首先完成任务的CompletableFuture对象
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() ->{
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "任务1";
        });
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() ->{
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "任务2";
        });
        //runAsync:没有结果返回，调用get()方法时返回null
        CompletableFuture<Void> future3 = CompletableFuture.runAsync(() ->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        });

        CompletableFuture.anyOf(future1,future2,future3).whenComplete((v, e) -> {
            System.out.println("最先执行结束的任务返回值是： " + v +"ex: " + e);
        });


        //thenAcceptBoth:用来组合两个 CompletableFuture，其中一个 CompletableFuture 等待另一个 CompletableFuture 的结果。
        CompletableFuture<Void> compose = future1.thenAcceptBoth(future2, (x, y) -> {
            System.out.println(x + ", " + y);
        });
        System.out.println(compose.get());


        executor.shutdown();



    }


}
